How to Access Environment Variables

Environment variables are a set of dynamic name-value pair set.
They are part of the operating environment in which a process runs and, therefore, they affect the way a running process behave.

Example: PATH used by shell to locate the program whenever no absolute path is provided

Old fashion

From main function

#include <stdio.h>
void main(int argc , char* argv[], char* envp[]){
	int i=0;
	while(envp[i]!=NULL){
		printf("%s\n",envp[i++]);
	}
}

envp[] is a NULL terminated array (as argv) and can be only be used by main.
Furthermore, envp[] is a snapshot of the environment variables.

Reliable way: environ

#include <stdio.h>

extern char** environ; 
void main(int argc , char* argv[], char* envp[]){
	int i=0;
	while(environ[i]!=NULL){
		printf("%s\n",envp[i++]);
	}
}

Now, environ is a global var so it can be used by all the function in such file.
Nevertheless, envrion is a box that contains the actual ENV: if something change inside it, environ "box" will change accordingly.

How does a Process get Environment Variables?

1. Using fork()

The child process will automatically inherit its parent process' environment variables.

2. Using execve()

In this scenario the memory space is overwritten by the data passed through the function. Therefore all the old env variables are lost.
However, by passing to enivron as envp[] args in the function call
execve(const char* filename, char* const argv[], char* const envp[])
we can pass the env variables to the new program.

If we want to:

  • pass no env variables to the new program -> envp = NULL
  • pass new env variables to the new program -> envp = newenv[] where newenv should be a NULL terminate array which contain name-value pairs.

Memory Location for Environment Variables

Environment variables are stored on the stack.

Pasted image 20250514150046.png

Both envp and environ initially point to the beginning of the environment variables array, (2 in figure).
However, the actual data of the environ variables is placed in another area (1 in figure).

Therefore, when changes are made to the environment vars (such as add, remove vars), there may not be enough space in 1 and 2 to store the new ones. This way result into a reallocation on the heap and only environ, which is a global variable “capture" such change by pointing to the new memory area. On the other hand, envp will not change and, so, it still point to the original area.

Important

Always choose environ over envp

Shell Vars VS ENV Vars

Shell variables are internal variables used by the shell.
We can:

  • create new variable (NAME=value)
bisca@ubuntu:$ FOO=bar
bisca@ubuntu:$ echo $FOO
bar
  • delete a variable (unset NAME)
bisca@ubuntu:$ unset FOO
bisca@ubuntu:$ echo $FOO

bisca@ubuntu:$ 

Shell Vars != ENV Vars

The confusion comes by the fact that shell vars can be env vars and vice-versa.
Indeed, when a shell program starts, it defines a shell var for each of the env vars, using the same name and copying their values.

From now on, any changes on shell variables will not affect env vars and vice-versa.

Example
# /proc/$$/environ -> Print the env vars of the current shell
bisca@ubuntu:$ strings /proc/$$/environ | grep LOGNAME 
LOGNAME = bisca # It is an env var

bisca@ubuntu:$ echo LOGNAME
bisca # <- which is a shell var !!!

bisca@ubuntu:$ LOGNAME = zanzi
bisca@ubuntu:$ echo $LOGNAME 
zanzi # <- shell var changed 

bisca@ubuntu:$ strings /proc/$$/environ | grep LOGNAME 
LOGNAME = bisca #Env var is the same, no change 

bisca@ubuntu:$ unset LOGNAME
bisca@ubuntu:$ echo $LOGNAME 

bisca@ubuntu:$ strings /proc/$$/environ | grep LOGNAME 
LOGNAME = bisca #Env var is the same, no change 

Shell Vars Affect ENV Vars of the child process

Pasted image 20250514155506.png

As we can see in the figure, the shell vars that are passed to the new process as ENV vars are:

  • shell variables copied from the ENV vars. If some shell vars is deleted by the unset command, it will not be copied in the new process ENV vars.
  • user-defined shell variables, marked with the export command
Example
bisca@ubuntu:$ strings /proc/$$/environ | grep LOGNAME 
LOGNAME = bisca # It is an env var

bisca@ubuntu:$ LOGNAME2 = zanzi
bisca@ubuntu: export LOGNAME = rick
# env in shell prompt will create a child process
bisca@ubuntu: env | grep LOGNAME
LOGNAME = bisca
LOGNAME3=rick

bisca@ubuntu:$ unset LOGNAME
bisca@ubuntu: env | grep LOGNAME
LOGNAME3=rick